/* gm_display.c -- Mostra as informações de jogo no terminal
 *
 * Copyright (C) 2008, Diogo F. S. Ramos <diogo.ramos@usp.br>
 * GEAR - Grupo de Estudos Avançados em Robótica
 * <http://www.sel.eesc.usp.br/gear>
 *
 * This file is part of GEARMedula.
 * 
 * GEARMedula is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, version 3 of the
 * License.
 * 
 * GEARMedula is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

#define _GNU_SOURCE

#include <time.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

#include "gm_monitor.h"
#include "gm_display.h"
#include "gm_utils.h"
#include "gm_protocol.h"

int mypipe[2];
FILE * instream;
FILE * outstream;
char * buff;
size_t buff_size;


/* Static Functions */
static void * gm_display_run( void * ptr );
static void gm_display_init_ncurses( gm_display_t * self );
static void gm_display_end_ncurses( gm_display_t * self );
static void gm_display_world_window_init( gm_display_t * self );
static void gm_display_field_window_init( gm_display_t * self );
static void gm_display_errinf_window_init( gm_display_t * self );
static void gm_display_world_window_update( gm_display_t * self );
static void gm_display_world_window_set_pos( gm_display_t * self, int x, int y, float data);
static void gm_display_field_window_update( gm_display_t * self );
static void gm_display_field_window_set_pos( gm_display_t * self, int x, int y, float data);
static void gm_display_enable( gm_display_t * self, const int enable );
static int gm_display_is_enable( gm_display_t *self );
static void gm_display_errinf_window_update( gm_display_t * self );

gm_display_t *
gm_display_new(gm_monitor_t * monitor)
{
    gm_display_t * self = NULL;
    int i = 0;

    self = XMALLOC( gm_display_t, 1 );

    /* Iniciando o mundo */
    self->world_size = LENGTH_WORLD_VECTOR;
    self->world = XMALLOC( float, self->world_size );
    for( i = 0 ; i < self->world_size ; i++ )
	self->world[i] = 0;
    self->world_window = NULL;
    
    /* Iniciando field */
    self->field_size = LENGTH_FIELD_VECTOR;
    self->field = XMALLOC( float, self->field_size );
    for( i = 0 ; i < self->field_size ; i++ )
	self->field[i] = 0;
    self->field_window = NULL;
    
    
    self->enable = 1;
    pthread_mutex_init( &(self->enable_mutex),
			NULL );

    self->monitor = monitor;
    
    return self;
}

void
gm_display_destroy( gm_display_t * self )
{
    if( gm_display_is_enable( self ) )
    {
	gm_display_enable(self, 0);
	return;
    }
    
    gm_display_end_ncurses( self );
    XFREE( self );
}

static void
gm_display_init_ncurses( gm_display_t * self )
{
    int col;
    int row;
    char mesg0[] = "GEAR - Grupo de Estudos Avançados em Robótica";
    char mesg1[] = "GEARMedula - Servidor";

    initscr();

    getmaxyx(stdscr,row,col);
    mvprintw(1,(col-strlen(mesg0))/2,"%s",mesg0);
    mvprintw(2,(col-strlen(mesg1))/2,"%s",mesg1);
    border('|', '|', '-', '-', '+', '+', '+', '+');
    refresh();

    gm_display_world_window_init( self );

    gm_display_field_window_init( self );

    gm_display_errinf_window_init( self );
}

static void
gm_display_world_window_init( gm_display_t * self )
{
    int row = 0;
    int col = 0;
    
    self->w_x0_pos = 5;
    self->w_y0_pos = 3;    
    self->w_x_inc = 8;
    self->world_nlines = 19;
    self->world_startx = 4;

    self->world_window = newwin(self->world_nlines,
				COLS/2,
				self->world_startx,
				1);
    getmaxyx(self->world_window,row,col);
    mvwprintw(self->world_window,1,(col-strlen("WORLD"))/2,"%s", "WORLD");
    
    mvwprintw(self->world_window,4,2,"x0");
    mvwprintw(self->world_window,5,2,"y0");
    mvwprintw(self->world_window,6,2,"x1");
    mvwprintw(self->world_window,7,2,"y1");
    mvwprintw(self->world_window,8,2,"x2");
    mvwprintw(self->world_window,9,2,"y2");
    mvwprintw(self->world_window,10,2,"x3");
    mvwprintw(self->world_window,11,2,"y3");

    mvwprintw(self->world_window,self->w_y0_pos,self->w_x0_pos,"Mu");
    mvwprintw(self->world_window,self->w_y0_pos,self->w_x0_pos + (self->w_x_inc*1),"Ca");
    mvwprintw(self->world_window,self->w_y0_pos,self->w_x0_pos + (self->w_x_inc*2),"Me");
    mvwprintw(self->world_window,self->w_y0_pos,self->w_x0_pos + (self->w_x_inc*3),"G0");
    mvwprintw(self->world_window,self->w_y0_pos,self->w_x0_pos + (self->w_x_inc*4),"G1");

    box(self->world_window, 0, 0);
}

static void
gm_display_field_window_init( gm_display_t * self )
{
    int row = 0;
    int col = 0;
    
    self->f_x0_pos = 5;
    self->f_y0_pos = 3;    
    self->f_x_inc = 8;
    self->field_nlines = 19;
    self->field_startx = 4;

    self->field_window = newwin(self->field_nlines,
				COLS/2-1,
				self->field_startx,
				COLS/2+1);
    getmaxyx(self->field_window,row,col);
    mvwprintw(self->field_window,1,(col-strlen("FIELD"))/2,"%s", "FIELD");

    mvwprintw(self->field_window,self->f_y0_pos,self->f_x0_pos,"px");
    mvwprintw(self->field_window,self->f_y0_pos,self->f_x0_pos + (self->f_x_inc*1),"py");
    mvwprintw(self->field_window,self->f_y0_pos,self->f_x0_pos + (self->f_x_inc*2),"pa");


    mvwprintw(self->field_window,4,2,"G0");
    mvwprintw(self->field_window,5,2,"G1");
    mvwprintw(self->field_window,6,2,"G2");
    mvwprintw(self->field_window,7,2,"G3");
    mvwprintw(self->field_window,8,2,"G4");

    mvwprintw(self->field_window,10,2,"A0");
    mvwprintw(self->field_window,11,2,"A1");
    mvwprintw(self->field_window,12,2,"A2");
    mvwprintw(self->field_window,13,2,"A3");
    mvwprintw(self->field_window,14,2,"A4");

    mvwprintw(self->field_window,16,2,"BL");

    
    box(self->field_window, 0, 0);
}

static void
gm_display_errinf_window_init( gm_display_t * self )
{
    int row = 0;
    int col = 0;

    getmaxyx(self->world_window,row,col);
    self->errinf_window = newwin( LINES-self->field_nlines-self->field_startx-1,
				  COLS-2,
				  self->field_startx + self->field_nlines,
				  1 );
    scrollok( self->errinf_window, 1 );

    /* Aqui vai uma das maiores proezas da humanidade! */
    fclose( stderr );
    stderr = open_memstream( &buff, &buff_size );
    xerror_info("display: Display pronto.");
}

static void
gm_display_world_window_set_pos( gm_display_t * self,
				 int x,
				 int y,
				 float data)
{
    mvwprintw(self->world_window,
	      (self->w_y0_pos+1) + y,
	      (self->w_x0_pos) + (self->w_x_inc * x),
	      "%.2f",data);

}

static void
gm_display_field_window_set_pos( gm_display_t * self,
					    int x,
					    int y,
					    float data)
{
    mvwprintw(self->field_window,
	      (self->f_y0_pos+1) + y,
	      (self->f_x0_pos) + (self->f_x_inc * x),
	      "%.2f",data);

}

static void
gm_display_field_window_update( gm_display_t * self )
{
    int i = 0;
    int j = 0;
    int index = 0;

    /* Robôs */
    for( i = 0 ; i < 11 ; i++ )
    {
	if( i == 5 )
	    i++;
	for( j = 0 ; j < 3 ; j++ )
	{
	    gm_display_field_window_set_pos( self,
					     j,
					     i,
					     self->field[index+2] );
	    index++;
	}
    }

    /* Bola */
    for( i = 0 ; i < 2 ; i++ )
	gm_display_field_window_set_pos( self,
					 i,
					 12,
					 self->field[i] );

}

static void
gm_display_world_window_update( gm_display_t * self )
{
    int i = 0;

    /* Mundo */
    for( i = 0 ; i < 8 ; i++ )
	gm_display_world_window_set_pos( self,
					 0,
					 i,
					 self->world[i] );

    /* Campo */
    for( i = 0 ; i < 8 ; i++ )
	gm_display_world_window_set_pos( self,
					 1,
					 i,
					 self->world[i+8] );

    /* Meio */
    for( i = 0 ; i < 4 ; i++ )
	gm_display_world_window_set_pos( self,
					 2,
					 i,
					 self->world[i+16] );

    /* Gol 0 */
    for( i = 0 ; i < 4 ; i++ )
	gm_display_world_window_set_pos( self,
					 3,
					 i,
					 self->world[i+20] );

    /* Gol 1 */
    for( i = 0 ; i < 4 ; i++ )
	gm_display_world_window_set_pos( self,
					 4,
					 i,
					 self->world[i+24] );
}



static void
gm_display_errinf_window_update( gm_display_t * self )
{
    wprintw( self->errinf_window, "%s", buff);
    fflush( stderr );
    rewind( stderr );
    fflush( stderr );

}

static void
gm_display_end_ncurses( gm_display_t * self )
{
    delwin( self->world_window );
    delwin( self->field_window );
    delwin( self->errinf_window );
    endwin();
}

static void *
gm_display_run( void * ptr )
{
    gm_display_t * self = (gm_display_t*) ptr;
    int i = 0;
    
    gm_display_init_ncurses( self );

    while( 1 )
    {
	gm_monitor_get_world( self->monitor, self->world );
	gm_display_world_window_update( self );

	gm_monitor_get_field( self->monitor, self->field );
	gm_display_field_window_update( self );

	gm_display_errinf_window_update( self );

	wrefresh( self->world_window );
	wrefresh( self->field_window );
	wrefresh( self->errinf_window );
	i++;
	if( i > 10 )
	    if( !gm_display_is_enable( self ) )
		break;
	
	sleep(1);
    }

    gm_display_destroy( self );
    pthread_exit(NULL);
    return NULL;
}

void
gm_display_enable( gm_display_t * self, const int enable )
{

    pthread_mutex_lock( &(self->enable_mutex) );

    if( enable )
	self->enable = 1;
    else
	self->enable = 0;

    pthread_mutex_unlock( &(self->enable_mutex) );

}

int
gm_display_is_enable( gm_display_t *self )
{
    int result = 0;
    
    pthread_mutex_lock( &(self->enable_mutex) );
    
    if( self->enable )
	result =  1;
    else
	result = 0;

    pthread_mutex_unlock( &(self->enable_mutex) );

    return result;
}

/** Inicia o display.
 *
 * @param *self Ponteiro para objeto gm_display_t.
 */
void
gm_display_init( gm_display_t * self )
{
    int iret1 = 0;

    iret1 = pthread_create( &(self->display_thread),
    			    NULL,
    			    gm_display_run,
    			    (void*) self );
}
